package com.mldong.modules.sys.service.impl;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mldong.base.CodedEnum;
import com.mldong.base.CommonPage;
import com.mldong.base.YesNoEnum;
import com.mldong.consts.CommonConstant;
import com.mldong.dict.CustomDictService;
import com.mldong.dict.DictScanner;
import com.mldong.dict.model.DictItemModel;
import com.mldong.dict.model.DictModel;
import com.mldong.excel.IMyExcelExportServer;
import com.mldong.exception.ServiceException;
import com.mldong.modules.sys.cache.DictCache;
import com.mldong.modules.sys.dto.DictItemParam;
import com.mldong.modules.sys.dto.DictPageParam;
import com.mldong.modules.sys.dto.DictParam;
import com.mldong.modules.sys.entity.Dict;
import com.mldong.modules.sys.entity.DictItem;
import com.mldong.modules.sys.enums.DictDataType;
import com.mldong.modules.sys.mapper.DictMapper;
import com.mldong.modules.sys.service.DictItemService;
import com.mldong.modules.sys.service.DictService;
import com.mldong.modules.sys.vo.DictVO;
import com.mldong.util.LowCodeServiceUtil;
import com.mldong.util.PoiUtil;
import com.mldong.util.RedisUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 字典 服务实现类
 * </p>
 *
 * @author mldong
 * @since 2023-09-21
 */
@Service
@RequiredArgsConstructor
public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements DictService, IMyExcelExportServer<DictParam,DictPageParam> {
    private final DictScanner dictScanner;
    private final DictItemService dictItemService;
    private final DictCache dictCache;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(DictParam param) {
        param.setId(null);
        LowCodeServiceUtil.checkUnique(baseMapper,"code",param.getCode(),null,"唯一编码已存在，请检查code参数");
        Dict dict = new Dict();
        BeanUtil.copyProperties(param, dict);
        boolean success = super.save(dict);
        if(success && CollectionUtil.isEmpty(param.getDictItemList())){
            param.getDictItemList().forEach(item->{
                item.setDictId(dict.getId());
                dictItemService.save(item);
            });
        }
        return success;
    }

    @Override
    public boolean update(DictParam param) {
        LowCodeServiceUtil.checkUnique(baseMapper,"code",param.getCode(),param.getId(),"唯一编码已存在，请检查code参数");
        Dict dict = new Dict();
        BeanUtil.copyProperties(param, dict);
        return RedisUtil.delayedDoubleRemove(dictCache,(Dict object)->{
            boolean success = super.updateById(object);
            if(success && CollectionUtil.isNotEmpty(param.getDictItemList())){
                List<Long> ids = new ArrayList<>();
                param.getDictItemList().forEach(item->{
                    item.setDictId(dict.getId());
                    try{
                        dictItemService.save(item);
                    } catch (ServiceException e){
                        DictItem dictItem = dictItemService.getOne(Wrappers.lambdaQuery(DictItem.class).eq(DictItem::getCode,item.getCode()));
                        item.setId(dictItem.getId());
                        dictItemService.update(item);
                    }
                    ids.add(item.getId());
                });
                dictItemService.remove(
                        Wrappers.lambdaQuery(DictItem.class)
                                .eq(DictItem::getDictId,dict.getId())
                                .notIn(DictItem::getId,ids));

            }
            return success;
        },dict,"code");
    }

    @Override
    public boolean removeByIds(Collection<?> list) {
        List<Dict> dictList = baseMapper.selectBatchIds((Collection<? extends Serializable>) list);
        return RedisUtil.delayedDoubleRemove(dictCache,(List<Dict> objects)->{
            return super.removeByIds(list);
        },dictList,"code");
    }

    @Override
    public CommonPage<DictVO> page(DictPageParam param) {
        IPage<DictVO> page = param.buildMpPage();
        QueryWrapper queryWrapper = param.buildQueryWrapper();
        queryWrapper.eq("t.is_deleted",YesNoEnum.NO);
        List<DictVO> list = baseMapper.selectCustom(page, queryWrapper);
        page.setRecords(list);
        return CommonPage.toPage(page);
    }
    @Override
    public DictVO findById(Long id) {
        return baseMapper.findById(id);
    }

    @Override
    public List<cn.hutool.core.lang.Dict> getByDictType(String dictType) {
        DictModel res = dictCache.get(dictType);
        if(ObjectUtil.isNotNull(res)) return dictModelToDictList(res);
        DictModel dictModel;
        DictDataType dictDataType;
        Dict dict = this.getOne(Wrappers.lambdaQuery(Dict.class)
                .eq(Dict::getCode, dictType)
                .eq(Dict::getEnabled,YesNoEnum.YES)
        );
        if (ObjectUtil.isNull(dict)) {
            // 空，则使用枚举类
            dictModel = dictScanner.getDictMap().get(dictType);
            if (dictModel == null) {
                String[] customDictServiceArr = SpringUtil.getBeanNamesForType(CustomDictService.class);
                // 这里是自定义范围，由业务模块实现
                for (String name : customDictServiceArr) {
                    CustomDictService customDictService = SpringUtil.getBean(name);
                    Map<String, Object> args = new HashMap<>();
                    args.put("dictType", dictType);
                    dictModel = customDictService.getByDictKey(args);
                    if(dictModel != null) {
                        break;
                    }
                }
            }
            if (dictModel == null) {
                return new ArrayList<>();
            } else {
                //抽取code和value封装到map返回
                return dictModelToDictList(dictModel);
            }
        } else {
            dictDataType = CodedEnum.codeOf(DictDataType.class,dict.getDataType()).orElse(DictDataType.STRING);
        }
        List<cn.hutool.core.lang.Dict> dicts = dictItemService.getDictItemListByDictId(dict.getId(), dictDataType);
        if(!CollectionUtil.isEmpty(dicts)){
            dictCache.put(dictType, dictListToDictModel(dict, dicts));
        }
        return dicts;
    }

    @Override
    public String toLabel(String dictType, Object value) {
        if(value==null) return null;
        List<cn.hutool.core.lang.Dict> dicts = getByDictType(dictType);
        for (cn.hutool.core.lang.Dict dict : dicts) {
            if (dict.getStr(CommonConstant.VALUE).equals(StrUtil.toString(value))) {
                return dict.getStr(CommonConstant.LABEL);
            }
        }
        return StrUtil.toString(value);
    }

    @Override
    public Object toValue(String dictType, String label) {
        if(StrUtil.isEmpty(label)) return null;
        List<cn.hutool.core.lang.Dict> dicts = getByDictType(dictType);
        for (cn.hutool.core.lang.Dict dict : dicts) {
            if (dict.getStr(CommonConstant.LABEL).equals(label)) {
                return dict.get(CommonConstant.VALUE);
            }
        }
        return label;
    }

    @Override
    public void clearCache() {
        dictCache.getAllKeys().forEach(dictCache::remove);
    }

    @Override
    public void importTo(InputStream inputStream) {
        List<DictParam> list = PoiUtil.importExcel(inputStream, DictParam.class,2);
        list.forEach(dictParam -> {
            try{
                save(dictParam);
            } catch (ServiceException e){
                Dict dict = baseMapper.selectOne(Wrappers.lambdaQuery(Dict.class).eq(Dict::getCode,dictParam.getCode()));
                dictParam.setId(dict.getId());
                update(dictParam);
            }
        });
    }

    /**
     * 将字典模型对象转成字典列表
     *
     * @param dictModel
     * @return
     */
    private List<cn.hutool.core.lang.Dict> dictModelToDictList(DictModel dictModel) {
        List<cn.hutool.core.lang.Dict> dictList = CollectionUtil.newArrayList();
        dictModel.getItems().forEach(dictItemModel -> {
            cn.hutool.core.lang.Dict dict = cn.hutool.core.lang.Dict.create();
            dict.put(CommonConstant.VALUE, dictItemModel.getDictItemValue());
            dict.put(CommonConstant.LABEL, dictItemModel.getName());
            dictList.add(dict);
        });
        return dictList;
    }
    /**
     * 将字典列表转成字典模型对象
     * @param dict
     * @param dictList
     * @return
     */
    private DictModel dictListToDictModel(Dict dict, List<cn.hutool.core.lang.Dict> dictList) {
        DictModel dictModel = new DictModel();
        List<DictItemModel> items = dictList.stream().map(item -> {
            DictItemModel dictItemModel = new DictItemModel();
            dictItemModel.setDictItemKey(item.getStr(CommonConstant.VALUE));
            dictItemModel.setName(item.getStr(CommonConstant.LABEL));
            dictItemModel.setDictItemValue(item.get(CommonConstant.VALUE));
            return dictItemModel;
        }).collect(Collectors.toList());
        dictModel.setName(dict.getName());
        dictModel.setDictKey(dict.getCode());
        dictModel.setItems(items);
        return dictModel;
    }

    // 缓存数据，数据只缓存一分钟，相当于同一次导入、导出相同key的缓存
    private static final TimedCache<String,String> timedCacheLabel = CacheUtil.newTimedCache(DateUnit.MINUTE.getMillis());
    /**
     * 字典值转标签
     * @param dictType
     * @param value
     * @return
     */
    public static String dictValueToLabel(String dictType,Object value) {
        String key = StrUtil.format("{}-{}",dictType,value);
        if(timedCacheLabel.containsKey(key)) {
            return timedCacheLabel.get(key);
        }
        Object object = SpringUtil.getBean("dictServiceImpl");
        String label = ReflectUtil.invoke(object,"toLabel",dictType,value);
        timedCacheLabel.put(key,label);
        return label;
    }
    // 缓存数据，数据只缓存一分钟，相当于同一次导入、导出相同key的缓存
    private static final TimedCache<String,Object> timedCacheValue = CacheUtil.newTimedCache(DateUnit.MINUTE.getMillis());
    /**
     * 字典标签转值
     * @param dictType
     * @param label
     * @return
     */
    public static Object dictLabelToValue(String dictType,String label) {
        String key = StrUtil.format("{}-{}",dictType,label);
        if(timedCacheValue.containsKey(key)) {
            return timedCacheValue.get(key);
        }
        Object object = SpringUtil.getBean("dictServiceImpl");
        Object value = ReflectUtil.invoke(object,"toValue",dictType,label);
        timedCacheValue.put(key,value);
        return value;
    }

    @Override
    public List<DictParam> selectListForExcelExport(DictPageParam pageParam, int pageNum) {
        pageParam.setPageNum(pageNum);
        pageParam.setPageSize(1000);
        CommonPage<DictVO> commonPage = page(pageParam);
        List<DictVO> rows = commonPage.getRows();
        List<DictParam> dictParamList = new ArrayList<>();
        rows.forEach(row -> {
            DictParam dictParam = BeanUtil.toBean(row,DictParam.class);
            dictParam.setDictItemList(BeanUtil.copyToList(dictItemService.getDictItemListByDictId(row.getId()), DictItemParam.class));
            dictParamList.add(dictParam);
        });
        return dictParamList;
    }
}
